package com.inspectortime.webapp.inspector.fee;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.inspectortime.domain.InspectorCompany;
import com.inspectortime.domain.pricing.BaseFeeWithAdjustersCalculator;
import com.inspectortime.domain.pricing.FeeCalculator;
import com.inspectortime.domain.pricing.PricingPlanType;

public class FeeWizardBedBathHelper extends FeeWizardCommonHelper {
	
	private class ListEntry {
		public float baths;
		public int beds;
		public String field;
		public String label;
		public double price;
		public boolean equals(ListEntry o) {
			return o.field.equals(this.field);
		}
		public String toString() {
			return field + ": " + beds + " beds, " + baths + " baths"; 
		}
	}
	private static final String[][] bedBathPriceRangesWithLabels = {
			{ "1beds1bath", "1 Bedroom, 1 Bathroom", "1", "1" },
			{ "2beds1bath", "2 Bedrooms, 1 Bathroom", "2", "1" },
			{ "2beds1.5bath", "2 Bedrooms, 1.5 Bathrooms", "2", "1.5"  },
			{ "2beds2bath", "2 Bedrooms, 2 Bathrooms", "2", "2"  },
			{ "3beds1bath", "3 Bedrooms, 1 Bathroom", "3", "1"  },
			{ "3beds1.5bath", "3 Bedrooms, 1.5 Bathrooms", "3", "1.5"  },
			{ "3beds2bath", "3 Bedrooms, 2 Bathrooms", "3", "2"  },
			{ "3beds2.5bath", "3 Bedrooms, 2.5 Bathrooms", "3", "2.5"  },
			{ "3beds3bath", "3 Bedrooms, 3 Bathrooms", "3", "3"  },
			{ "4beds1bath", "4 Bedrooms, 1 Bathroom", "4", "1"  },
			{ "4beds1.5bath", "4 Bedrooms, 1.5 Bathrooms", "4", "1.5"  },
			{ "4beds2bath", "4 Bedrooms, 2 Bathrooms", "4", "2"  },
			{ "4beds2.5bath", "4 Bedrooms, 2.5 Bathrooms", "4", "2.5"  },
			{ "4beds3bath", "4 Bedrooms, 3 Bathrooms", "4", "3"  },
			{ "4beds3.5bath", "4 Bedrooms, 3.5 Bathrooms" ,"4", "3.5"  },
			{ "4beds4bath", "4 Bedrooms, 4 Bathrooms", "4", "4"  },
			{ "5beds1bath", "5 Bedrooms, 1 Bathroom" , "5", "1" },
			{ "5beds1.5bath", "5 Bedrooms, 1.5 Bathrooms", "5", "1.5"  },
			{ "5beds2bath", "5 Bedrooms, 2 Bathrooms", "5", "2"  },
			{ "5beds2.5bath", "5 Bedrooms, 2.5 Bathrooms" , "5", "2.5" },
			{ "5beds3bath", "5 Bedrooms, 3 Bathrooms", "5", "3"  },
			{ "5beds3.5bath", "5 Bedrooms, 3.5 Bathrooms" , "5", "3.5" },
			{ "5beds4bath", "5 Bedrooms, 4 Bathrooms" , "5", "4" },
			{ "5beds4.5bath", "5 Bedrooms, 4.5 Bathrooms", "5", "4.5"  },
			{ "5beds5bath", "5 Bedrooms, 5 Bathrooms" , "5", "5" },
			{ "6beds1bath", "6 Bedrooms, 1 Bathroom", "6", "1"  },
			{ "6beds1.5bath", "6 Bedrooms, 1.5 Bathrooms" , "6", "1.5" },
			{ "6beds2bath", "6 Bedrooms, 2 Bathrooms" , "6", "2" },
			{ "6beds2.5bath", "6 Bedrooms, 2.5 Bathrooms" , "6", "2.5" },
			{ "6beds3bath", "6 Bedrooms, 3 Bathrooms", "6", "3"  },
			{ "6beds3.5bath", "6 Bedrooms, 3.5 Bathrooms", "6", "3.5"  },
			{ "6beds4bath", "6 Bedrooms, 4 Bathrooms" , "6", "4" },
			{ "6beds4.5bath", "6 Bedrooms, 4.5 Bathrooms", "6", "4.5"  },
			{ "6beds5bath", "6 Bedrooms, 5 Bathrooms" , "6", "5" },
			{ "6beds5.5bath", "6 Bedrooms, 5.5 Bathrooms", "6", "5.5"  },
			{ "6beds6bath", "6 Bedrooms, 6 Bathrooms" , "6", "6" },
			};

	private static final float MAX_FLOAT = -1.0f;
	
	private static final int MAX_INT = -1;
	
	private List<ListEntry> entries = new ArrayList<ListEntry>();
	
	public FeeWizardBedBathHelper(double defaultPrice) {

		this.setType(PricingPlanType.BedsAndBaths);

		// initial bed/bath ranges
		ListEntry entry = null;
		for (int i = 0; i < bedBathPriceRangesWithLabels.length; i++) {
			entry = new ListEntry();
			entry.field = bedBathPriceRangesWithLabels[i][0];
			entry.label = bedBathPriceRangesWithLabels[i][1];
			try {
				entry.beds = Integer.parseInt(bedBathPriceRangesWithLabels[i][2]);	
			} catch (NumberFormatException e) {
				entry.beds = MAX_INT;
			}
			try {
				entry.baths = Float.parseFloat(bedBathPriceRangesWithLabels[i][3]);	
			} catch (NumberFormatException e) {
				entry.baths = MAX_FLOAT;
			}
			entry.price = defaultPrice;
			entries.add(entry);
		}
	}
	
	public String lookupLabel(String field) {
		for (int i = 0; i < bedBathPriceRangesWithLabels.length; i++) {
			if (bedBathPriceRangesWithLabels[i][0].equals(field)) {
				return bedBathPriceRangesWithLabels[i][1];
			}
		}
		return null;
	}
	
	public List<BaseFeeWithAdjustersCalculator> createFeeCalculators() {

		List<BaseFeeWithAdjustersCalculator> calculators = new ArrayList<BaseFeeWithAdjustersCalculator>();
		BaseFeeWithAdjustersCalculator calculator = null;
		for (ListEntry entry : entries) {
			
			// Special "other" rule 
			if (entry.beds == MAX_INT) {
				calculator = new BaseFeeWithAdjustersCalculator();
				calculators.add(calculator);
				calculator.setMinNumberOfBedrooms(7);
				calculator.setMaxNumberOfBedrooms(0);
				calculator.setMinNumberOfBathrooms(6.5f);
				calculator.setMaxNumberOfBathrooms(0);
				calculator.setBaseFee(entry.price);
				calculator.setSource(entry.field);
				
			} else {
				
				// Create new 
				calculator = new BaseFeeWithAdjustersCalculator();
				calculators.add(calculator);
				calculator.setMinNumberOfBedrooms(entry.beds);
				calculator.setMaxNumberOfBedrooms(entry.beds);
				calculator.setMinNumberOfBathrooms(entry.baths);
				calculator.setMaxNumberOfBathrooms(entry.baths);
				calculator.setBaseFee(entry.price);
				calculator.setSource(entry.field);
			}
		}
		return calculators;		
	}
	
	public void populateFromCalculators(InspectorCompany company) {	
		populateFromCompany(company);
		for (FeeCalculator calculator : company.getFeeCalculators()) {
			if (calculator.getSource() == null) {
				continue;
			}
			ListEntry entry = this.getEntry(calculator.getSource());
			if (entry == null) {
				continue;
			}
			entry.label = this.lookupLabel(calculator.getSource());
			entry.beds = calculator.getMinNumberOfBedrooms();
			if (calculator.getMinNumberOfBedrooms() != calculator.getMaxNumberOfBedrooms()) {
				throw new RuntimeException("Unexpected bedroom range, would expect same min/max since this calculator should have been created from this helper");
			}
			entry.baths = calculator.getMinNumberOfBathrooms();
			entry.price = calculator.getBaseFee();
		}
	}

	public Map<String, Double> getBedBathFees() {
		Map<String, Double> result = new LinkedHashMap<String, Double>();
		for (ListEntry entry : entries) {
			result.put(entry.field, entry.price);
		}
		return result;
	}
	
	public Map<String, String> getBedBathLabels() {
		Map<String, String> result = new LinkedHashMap<String, String>();
		for (ListEntry entry : entries) {
			result.put(entry.field, entry.label);
		}
		return result;
	}

	private ListEntry getEntry(String field) {
		for (ListEntry entry : entries) {
			if (entry.field.equals(field)) {
				return entry;
			}
		}
		return null;
	}

	public double getPrice(String field) {
		ListEntry entry = getEntry(field);
		return entry.price;
	}
			
	public void setPrice(String field, double price) {
		ListEntry entry = getEntry(field);
		entry.price = price;
	}
	
	public void sortEntries() {
		Collections.sort(entries, new Comparator() {
			public int compare(Object o1, Object o2) {
				ListEntry e1 = (ListEntry) o1;
				ListEntry e2 = (ListEntry) o2;
				float rank1 = (e1.beds * 100000) + (e1.baths * 100);
				float rank2 = (e2.beds * 100000) + (e2.baths * 100);
				return (int) (rank1 - rank2);
			}
		});
	}

}
